package com.fangngng.pushall.service.impl;

import com.fangngng.pushall.constant.ErrorCode;
import com.fangngng.pushall.dto.AddTopicRemindVO;
import com.fangngng.pushall.dto.AddTopicVO;
import com.fangngng.pushall.dto.EditTopicRemindTimeVO;
import com.fangngng.pushall.dto.EditTopicRemindVO;
import com.fangngng.pushall.dto.EditTopicVO;
import com.fangngng.pushall.dto.RemindVO;
import com.fangngng.pushall.dto.RequestTopicVO;
import com.fangngng.pushall.dto.Result;
import com.fangngng.pushall.dto.TopicQuery;
import com.fangngng.pushall.dto.TopicVO;
import com.fangngng.pushall.entity.Remind;
import com.fangngng.pushall.entity.RemindTime;
import com.fangngng.pushall.entity.Tag;
import com.fangngng.pushall.entity.Topic;
import com.fangngng.pushall.entity.TopicTag;
import com.fangngng.pushall.entity.User;
import com.fangngng.pushall.entity.UserSubcribe;
import com.fangngng.pushall.exception.BusinessException;
import com.fangngng.pushall.mapper.RemindMapper;
import com.fangngng.pushall.mapper.RemindTimeMapper;
import com.fangngng.pushall.mapper.TagMapper;
import com.fangngng.pushall.mapper.TopicMapper;
import com.fangngng.pushall.mapper.TopicTagMapper;
import com.fangngng.pushall.mapper.UserMapper;
import com.fangngng.pushall.mapper.UserSubcribeMapper;
import com.fangngng.pushall.service.TopicService;
import com.fangngng.pushall.service.UserService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * @author fei.fang
 * @date 2020/7/2
 **/
@Service
@Slf4j
public class TopicServiceImpl implements TopicService {

    @Autowired
    private TopicMapper topicMapper;

    @Autowired
    private RemindMapper remindMapper;

    @Autowired
    private RemindTimeMapper remindTimeMapper;

    @Autowired
    private UserSubcribeMapper userSubcribeMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private TopicTagMapper topicTagMapper;

    @Autowired
    private UserService userService;

    @Autowired
    private TagMapper tagMapper;

    private LoadingCache<String, List<Topic>> topicCache = CacheBuilder.newBuilder()
            .expireAfterAccess(3600, TimeUnit.SECONDS)
            .build(new CacheLoader<String, List<Topic>>() {
                @Override
                public List<Topic> load(String key) {
                    if ("hot10".equalsIgnoreCase(key)) {
                        return topicMapper.getHottest10Topic();
                    }
                    if ("new10".equalsIgnoreCase(key)) {
                        return topicMapper.getNewest10Topic();
                    }
                    return new ArrayList<>();
                }
            });

    /**
     * 最热的 10 个 topic
     *
     * @return
     */
    @Override
    public List<Topic> getHottest10Topic() {
        List<Topic> hottest10Topic = null;
        try {
            hottest10Topic = topicCache.get("hot10");
        } catch (ExecutionException e) {
            log.error("", e);
        }
        return hottest10Topic;
    }

    /**
     * 最新的 10 个 topic
     *
     * @return
     */
    @Override
    public List<Topic> getNewtest10Topic() {
        List<Topic> newest10Topic = null;
        try {
            newest10Topic = topicCache.get("new10");
        } catch (ExecutionException e) {
            log.error("", e);
        }
        return newest10Topic;
    }

    @Override
    public Result<String> subscribeTopic(String topicCode, String userEmail) {

        if (org.springframework.util.StringUtils.isEmpty(topicCode)) {
            log.warn("订阅时code为空");
            return Result.fail("订阅时code为空");
        }
        Topic topic = this.getTopicByCode(topicCode);
        if (topic == null) {
            log.warn("订阅的主题不存在");
            return Result.fail("订阅的主题不存在");
        }

        User user = new User();
        user.setEmail(userEmail);
        User userdb = userMapper.selectOne(user);
        if (userdb == null) {
            log.warn("用户:{}不存在", userdb.getEmail());
            throw new BusinessException(ErrorCode.ERROR_LOGIN_USER_NOT_EXIST);
        }

        // 如果没有订阅过，新增订阅信息，如果已经订阅过，则更改状态
        UserSubcribe userSubcribe = new UserSubcribe();
        userSubcribe.setTopicId(topic.getId());
        userSubcribe.setUserId(userdb.getId());
        UserSubcribe usDb = userSubcribeMapper.selectOne(userSubcribe);
        if (usDb == null) {
            topic.setSubCount(topic.getSubCount() + 1);
            topicMapper.updateByPrimaryKeySelective(topic);

            userSubcribe.setCreateTime(new Date());
            userSubcribe.setStatus(UserSubcribe.LIFECYCLE_ENABLE);
            userSubcribe.setTopicId(topic.getId());
            userSubcribe.setUserId(userdb.getId());
            userSubcribe.setEmail(userdb.getEmail());
            userSubcribeMapper.insert(userSubcribe);
        }else{
            if (usDb.getStatus().equals(UserSubcribe.LIFECYCLE_DISABLE)) {
                topic.setSubCount(topic.getSubCount() + 1);
                topicMapper.updateByPrimaryKeySelective(topic);

                usDb.setStatus(UserSubcribe.LIFECYCLE_ENABLE);
                userSubcribeMapper.updateByPrimaryKeySelective(usDb);
            }
        }

        return Result.ok();
    }

    @Override
    public Result<String> unSubscribeTopic(String userEmail, String topicCode) {

        if (StringUtils.isEmpty(topicCode)) {
            log.warn("订阅时code为空");
            return Result.fail("订阅时code为空");
        }
        Topic topic = this.getTopicByCode(topicCode);
        if (topic == null) {
            log.warn("订阅的主题不存在");
            return Result.fail("订阅的主题不存在");
        }

        User user = new User();
        user.setEmail(userEmail);
        User userdb = userMapper.selectOne(user);
        if (userdb == null) {
            log.warn("用户:{}不存在", userdb.getEmail());
            throw new BusinessException(ErrorCode.ERROR_LOGIN_USER_NOT_EXIST);
        }

        topic.setSubCount(topic.getSubCount() - 1);
        topicMapper.updateByPrimaryKeySelective(topic);

        UserSubcribe us = new UserSubcribe();
        us.setUserId(userdb.getId());
        us.setTopicId(topic.getId());
        UserSubcribe userSubcribe = userSubcribeMapper.selectOne(us);
        if (userSubcribe == null) {
            log.warn("用户订阅记录不存在: email:{}, topci:{}", userEmail, topicCode);
            return Result.ok();
        }
        userSubcribe.setStatus(UserSubcribe.LIFECYCLE_DISABLE);
        userSubcribeMapper.updateByPrimaryKeySelective(userSubcribe);

        return Result.ok();
    }

    @Override
    public Topic getTopicByCode(String code) {
        Topic topic = new Topic();
        topic.setCode(code);
        return topicMapper.selectOne(topic);
    }

    @Override
    public TopicVO getTopicVOByCode(String code) {
        TopicVO topicVO = new TopicVO();
        Topic topic = this.getTopicByCode(code);
        if (topic != null && topic.getId() != null) {
            BeanUtils.copyProperties(topic, topicVO);
            Remind search = new Remind();
            search.setTopicId(topic.getId());
            List<Remind> remindList = remindMapper.select(search);
            List<RemindVO> remindVOList = new ArrayList<>();
            if (!remindList.isEmpty()) {
                for (Remind remind : remindList) {
                    RemindVO remindVO = new RemindVO();
                    BeanUtils.copyProperties(remind, remindVO);
                    RemindTime remindTime = new RemindTime();
                    remindTime.setRemindId(remind.getId());
                    List<RemindTime> remindTimeList = remindTimeMapper.select(remindTime);
                    remindVO.setRemindTimeList(remindTimeList);
                    remindVOList.add(remindVO);
                }
                // todo batch search time
//                remindList.stream().map(Remind::getId)
            }
            topicVO.setRemindList(remindVOList);

            List<Tag> tags = tagMapper.findTagByTopicId(topic.getId());
            topicVO.setTagList(tags);
        }
        return topicVO;
    }

    @Override
    public PageInfo<Topic> getTopicByPage(int pageNumber, int pageSize, String sort, TopicQuery topicQuery) {
        PageHelper.startPage(pageNumber, pageSize);
        PageHelper.orderBy(sort);
        List<Topic> topicList = topicMapper.findByCondition(topicQuery);
        PageInfo pageInfo = new PageInfo<>(topicList, pageSize);
        return pageInfo;
    }

    @Override
    public PageInfo<Topic> getSubTopicByPage(int pageNumber, int pageSize, String sort, TopicQuery topicQuery) {
        PageHelper.startPage(pageNumber, pageSize);
        PageHelper.orderBy(sort);
        List<Topic> topicList = topicMapper.findSubscribeTopicByCondition(topicQuery);
        PageInfo pageInfo = new PageInfo<>(topicList, pageSize);
        return pageInfo;
    }

    @Override
    public Topic addTopic(AddTopicVO addTopicVO, String email) {
        String name = addTopicVO.getName();
        String remark = addTopicVO.getText();
        List<Integer> tagIdList = addTopicVO.getTagIdList();
        User userDb = userService.findByEmail(email);
        if (userDb == null) {
            log.warn("用户:{}不存在", email);
            throw new BusinessException(ErrorCode.ERROR_LOGIN_USER_NOT_EXIST);
        }

        Topic topic = new Topic();
        topic.setName(name);
        topic.setRemark(remark);
        Date now = new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd");
        String today = simpleDateFormat.format(now);
        topic.setCode(today + RandomStringUtils.randomAlphabetic(5));
        topic.setCreateId(userDb.getId());
        topic.setCreateTime(now);
        topic.setSubCount(0);
        topic.setStatus(Topic.STATUS_ON);
        topicMapper.insert(topic);

        // 添加标签
        if (!CollectionUtils.isEmpty(tagIdList)) {
            // todo batch
            for (Integer tagId : tagIdList) {
                TopicTag topicTag = new TopicTag();
                topicTag.setTagId(tagId);
                topicTag.setTopicId(topic.getId());
                topicTagMapper.insert(topicTag);
            }
        }
        return topic;
    }

    @Override
    public Result<String> editTopic(EditTopicVO editTopicVO, String userEmail) {
        Integer topicId = editTopicVO.getId();
        if (topicId == null || StringUtils.isBlank(userEmail)) {
            throw new BusinessException(ErrorCode.ERROR_PARAM_ERROR);
        }

        Topic topicDb = topicEditCheck(topicId, userEmail, ErrorCode.ERROR_TOPIC_EDIT_ERROR);

        String name = editTopicVO.getName();
        String remark = editTopicVO.getText();
        if (StringUtils.isNotBlank(name)) {
            topicDb.setName(name);
        }
        if (StringUtils.isNotBlank(remark)) {
            topicDb.setRemark(remark);
        }
        topicMapper.updateByPrimaryKeySelective(topicDb);

        return Result.ok();
    }

    @Override
    public Result<String> deleteTopic(Integer topicId, String userEmail) {
        if (topicId == null || StringUtils.isBlank(userEmail)) {
            throw new BusinessException(ErrorCode.ERROR_PARAM_ERROR);
        }

        // 主题编辑检查
        Topic topicDb = topicEditCheck(topicId, userEmail, ErrorCode.ERROR_TOPIC_DELETE_ERROR);

        topicDb.setStatus(Topic.STATUS_DELETE);
        topicMapper.updateByPrimaryKeySelective(topicDb);

        // todo 发布主题删除消息，可能需要推送给用户时注明

        return Result.ok();
    }

    /**
     * 主题编辑检查
     * @param topicId
     * @param userEmail
     * @param errorTopicDeleteError
     * @return
     */
    private Topic topicEditCheck(Integer topicId, String userEmail, ErrorCode errorTopicDeleteError) {
        Topic topic = new Topic();
        topic.setId(topicId);
        Topic topicDb = topicMapper.selectOne(topic);
        if (topicDb == null) {
            log.warn("主题不存在, {}", topicId);
            throw new BusinessException(ErrorCode.ERROR_TOPIC_NOT_EXIST);
        }

        User userDb = userService.findByEmail(userEmail);
        if (userDb == null) {
            log.warn("用户:{}不存在", userEmail);
            throw new BusinessException(ErrorCode.ERROR_LOGIN_USER_NOT_EXIST);
        }

        if (!userDb.getId().equals(topicDb.getCreateId())) {
            log.warn("非本人主题，不能操作, email:{}, topic:{}", userEmail, topicId);
            throw new BusinessException(errorTopicDeleteError);
        }
        return topicDb;
    }

    @Override
    public Remind addTopicRemind(AddTopicRemindVO remindVO, String email) {
        User userDb = userService.findByEmail(email);
        if (userDb == null) {
            log.warn("用户:{}不存在", email);
            throw new BusinessException(ErrorCode.ERROR_LOGIN_USER_NOT_EXIST);
        }

        Integer topicId = Integer.valueOf(remindVO.getTopicId());
        Topic search = new Topic();
        search.setId(topicId);
        Topic topic = topicMapper.selectOne(search);
        if (topic == null) {
            log.warn("添加topicRemind时，传入的topic不存在:{}", topicId);
            throw new BusinessException(ErrorCode.ERROR_TOPIC_NOT_EXIST);
        }
        String name = remindVO.getName();
        String remark = remindVO.getText();

        Date now = new Date();
        Remind remind = new Remind();
        remind.setCreateId(userDb.getId());
        remind.setCreateTime(now);
        remind.setName(name);
        remind.setRemark(remark);
        remind.setTopicId(topicId);
        remind.setCode(topic.getCode() + "-" + RandomStringUtils.randomAlphabetic(2));
        remind.setStatus(Remind.STATUS_ON);
        remindMapper.insert(remind);

        // save remind time
        List<AddTopicRemindVO.RemindTimeVO> remindTimeList = remindVO.getRemindTimeList();
        if (remindTimeList == null || remindTimeList.isEmpty()) {
            return remind;
        }
        for (AddTopicRemindVO.RemindTimeVO timeVO : remindTimeList) {
            RemindTime time = new RemindTime();
            time.setAheadTime(StringUtils.isBlank(timeVO.getAheadTime()) ? null : timeVO.getAheadTime());
            String remindTime = timeVO.getRemindTime();
            Date date = null;
            try {
                date = DateUtils.parseDate(remindTime, "yyyy-MM-dd HH:mm");
            } catch (ParseException e) {
                log.error("", e);
            }
            time.setRemindTime(date);
            time.setRemindText(timeVO.getRemindTimeText());
            time.setRemindId(remind.getId());
            time.setStatus(RemindTime.STATUS_ON);
            remindTimeMapper.insert(time);
        }
        return remind;
    }

    @Override
    public Remind editTopicRemind(EditTopicRemindVO remindVO, String userEmail) {
        Integer remindId = remindVO.getRemindId();
        Integer topicId = remindVO.getTopicId();

        // 主题编辑检查
        topicEditCheck(topicId, userEmail, ErrorCode.ERROR_TOPIC_DELETE_ERROR);

        Remind remind = new Remind();
        remind.setId(remindVO.getRemindId());
        Remind remindDb = remindMapper.selectOne(remind);
        if (remindDb == null) {
            log.warn("提醒不存在, remind:{}", remindId);
            throw new BusinessException(ErrorCode.ERROR_TOPIC_REMIND_NOT_EXIST);
        }

        remindDb.setName(remindVO.getName());
        remindDb.setRemark(remindVO.getText());
        remindMapper.updateByPrimaryKeySelective(remindDb);

        return remindDb;
    }

    @Override
    public Result<String> deleteTopicRemind(Integer remindId, String email) {

        Remind remind = new Remind();
        remind.setId(remindId);
        Remind remindDb = remindMapper.selectOne(remind);
        if (remindDb == null) {
            log.warn("提醒不存在, remind:{}", remindId);
            throw new BusinessException(ErrorCode.ERROR_TOPIC_REMIND_NOT_EXIST);
        }

        // 主题编辑检查
        topicEditCheck(remindDb.getTopicId(), email, ErrorCode.ERROR_TOPIC_DELETE_ERROR);

        remindDb.setStatus(Remind.STATUS_DELETE);
        remindMapper.updateByPrimaryKeySelective(remindDb);

        return Result.ok();
    }

    @Override
    public RemindTime editTopicRemindTime(EditTopicRemindTimeVO remindVO, String email) {

        RemindTime remindTime = new RemindTime();
        remindTime.setId(remindVO.getRemindTimeId());
        RemindTime rtDb = remindTimeMapper.selectOne(remindTime);
        if (rtDb == null) {
            log.warn("提醒时间不存在, {}", remindVO.getRemindTimeId());
            throw new BusinessException(ErrorCode.ERROR_TOPIC_REMIND_TIME_NOT_EXIST);
        }

        Remind remind = new Remind();
        remind.setId(rtDb.getRemindId());
        Remind remindDb = remindMapper.selectOne(remind);
        if (remindDb == null) {
            log.warn("提醒不存在, remind:{}", rtDb.getRemindId());
            throw new BusinessException(ErrorCode.ERROR_TOPIC_REMIND_NOT_EXIST);
        }

        // 主题编辑检查
        topicEditCheck(remindDb.getTopicId(), email, ErrorCode.ERROR_TOPIC_DELETE_ERROR);

        rtDb.setRemindText(remindVO.getRemindText());
        String time = remindVO.getRemindTime();
        Date date = null;
        try {
            date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(time);
        } catch (ParseException e) {
            log.error("",e);
        }
        rtDb.setRemindTime(date);
        remindTimeMapper.updateByPrimaryKeySelective(rtDb);

        return rtDb;
    }

    @Override
    public Result<String> deleteTopicRemindTime(Integer remindTimeId, String email) {

        RemindTime remindTime = new RemindTime();
        remindTime.setId(remindTimeId);
        RemindTime rtDb = remindTimeMapper.selectOne(remindTime);
        if (rtDb == null) {
            log.warn("提醒时间不存在, {}", remindTimeId);
            throw new BusinessException(ErrorCode.ERROR_TOPIC_REMIND_TIME_NOT_EXIST);
        }

        Remind remind = new Remind();
        remind.setId(rtDb.getRemindId());
        Remind remindDb = remindMapper.selectOne(remind);
        if (remindDb == null) {
            log.warn("提醒不存在, remind:{}", rtDb.getRemindId());
            throw new BusinessException(ErrorCode.ERROR_TOPIC_REMIND_NOT_EXIST);
        }

        // 主题编辑检查
        topicEditCheck(remindDb.getTopicId(), email, ErrorCode.ERROR_TOPIC_DELETE_ERROR);

        rtDb.setStatus(RemindTime.STATUS_DELETE);
        remindTimeMapper.updateByPrimaryKeySelective(rtDb);

        return Result.ok();
    }

    @Override
    public Integer saveTopic(Topic topic) {
        Topic topicDb = this.getTopicByCode(topic.getCode());
        if (topicDb == null) {
            return topicMapper.insert(topic);
        }
        topic.setId(topicDb.getId());
        return topicMapper.updateByPrimaryKey(topic);
    }

    @Override
    public Integer saveSubscribe(UserSubcribe userSubcribe) {
        Topic userSubDb = userSubcribeMapper.findByUserIdAndTopicId(userSubcribe.getUserId(), userSubcribe.getTopicId());
        if (userSubDb == null) {
            return userSubcribeMapper.insert(userSubcribe);
        }
        userSubcribe.setId(userSubDb.getId());
        return userSubcribeMapper.updateByPrimaryKey(userSubcribe);
    }

    @Override
    public List<Topic> getUserSubscribeTopic(String userName) {
        return topicMapper.findByUserName(userName);
    }

    @Override
    public PageInfo<Topic> findPage(RequestTopicVO requestVO) {
        PageHelper.startPage(requestVO.getPageNumber(), requestVO.getPageSize());
        PageHelper.orderBy(requestVO.getSort());
        List<Topic> result = topicMapper.findTopic(requestVO);
        PageInfo<Topic> pageInfo = new PageInfo<>(result, requestVO.getPageSize());
        return pageInfo;
    }
}
